ESL Dataset
load("../data/mixture.example.RData")
intercept = 50.0
coef_x1 = 10.0
coef_x2 = -0.0
set.seed(1000)
df <- data.frame(x1 = mixture.example$x[,1],
x2 = mixture.example$x[,2]) %>%
mutate(y = intercept + coef_x1 * x1 + coef_x2 * x2 + rnorm(length(x1), 0, 5))
x.grid <- seq(min(df$x1), max(df$x1), 0.1)
y.grid <- seq(min(df$x2), max(df$x2), 0.1)
hist(df$y)

Figure (just the dataset)
xy.grid <- expand.grid(x.grid, y.grid)
names(xy.grid) <- c("x1", "x2")
p <- ggplot(df) +
geom_point(aes(x = x1, y = x2, colour = y), pch = 21, fill = NA) +
geom_point(aes(x = x1, y = x2), alpha = 0.5, data = xy.grid, colour = "gray50", size = 0.02) +
theme_minimal() +
xlab("Disease Severity Score (x1)") +
ylab("Social Determinants Score (x2)") +
theme(axis.text = element_blank()) +
theme(legend.position = "bottom", panel.grid.major = element_blank(), panel.grid.minor = element_blank()) +
scale_colour_gradient(name = "Recurrence\nBiomarker (ng/dL)", low = "#00bfc4", high = "#f8766d")
print(p)
ggsave(p, filename = "../img/esl-reg-just-data.png", height=4.5, width=4, units="in", dpi=300)

3D plot with KNN (K=15) as 3rd dimension
p <- plot_ly(
df, x = ~x1, y = ~x2, z = ~y,
color = ~y, colors = colorRamp(c("#00bfc4", "#f8766d"))) %>%
add_markers() %>%
layout(
scene = list(xaxis = list(title = 'Disease Severity Score (x1)'),
yaxis = list(title = 'Social Determinants Score (x2)'),
zaxis = list(title = 'Recurrence Biomarker (y)'))
)
print(p)
NULL
Decision boundary: logistic regression
m <- glm(y ~ x1 + x2, data = df, family = "binomial")
xy.grid <- expand.grid(x.grid, y.grid)
names(xy.grid) <- c("x1", "x2")
xy.grid$yhat <- predict(m, xy.grid, type = "response")
p <- ggplot(df) +
geom_point(aes(x = x1, y = x2, colour = y > 0.5), pch = 21, fill = NA) +
geom_point(aes(x = x1, y = x2, colour = yhat > 0.5), alpha = 0.5, data = xy.grid, size = 0.02) +
theme_minimal() +
xlab("Disease Severity Score (x1)") +
ylab("Social Determinants Score (x2)") + theme(axis.text = element_blank()) +
scale_colour_manual(name = "ER Admission?", labels = c("no", "yes"), values = c("#00bfc4", "#f8766d")) +
stat_contour(aes(x = x1, y = x2, z = yhat), breaks = 0.5, data = xy.grid, colour = "gray20") +
theme(legend.position = "none", panel.grid.major = element_blank(), panel.grid.minor = element_blank())
ggsave(p, filename = "../img/esl-logistic.png", height=4, width=4, units="in", dpi=300)
Logistic regression with probabilities.
m <- glm(y ~ x1 + x2, data = df, family = "binomial")
xy.grid <- expand.grid(x.grid, y.grid)
names(xy.grid) <- c("x1", "x2")
xy.grid$yhat <- predict(m, xy.grid, type = "response")
p <- ggplot(df) +
geom_point(aes(x = x1, y = x2, colour = y), pch = 21, fill = NA) +
geom_point(aes(x = x1, y = x2, colour = yhat), alpha = 1.0, data = xy.grid, size = 0.02) +
scale_colour_gradient(low = "#00bfc4", high = "#f8766d") +
theme_minimal() +
xlab("Disease Severity Score (x1)") +
ylab("Social Determinants Score (x2)") + theme(axis.text = element_blank()) +
stat_contour(aes(x = x1, y = x2, z = yhat), breaks = 0.5, data = xy.grid, colour = "gray20",
linetype = "dashed") +
theme(legend.position = "none", panel.grid.major = element_blank(), panel.grid.minor = element_blank())
ggsave(p, filename = "../img/esl-logistic-prob.png", height=4, width=4, units="in", dpi=300)
Logistic regression with probabilities and axis labels.
m <- glm(y ~ x1 + x2, data = df, family = "binomial")
xy.grid <- expand.grid(x.grid, y.grid)
names(xy.grid) <- c("x1", "x2")
xy.grid$yhat <- predict(m, xy.grid, type = "response")
p <- ggplot(df) +
geom_point(aes(x = x1, y = x2, colour = y), pch = 21, fill = NA) +
geom_point(aes(x = x1, y = x2, colour = yhat), alpha = 1.0, data = xy.grid, size = 0.02) +
scale_colour_gradient(low = "#00bfc4", high = "#f8766d") +
theme_minimal() +
xlab("Disease Severity Score (x1)") +
ylab("Social Determinants Score (x2)") +
stat_contour(aes(x = x1, y = x2, z = yhat), breaks = 0.5, data = xy.grid, colour = "gray20",
linetype = "dashed") +
theme(legend.position = "none")
ggsave(p, filename = "../img/esl-logistic-prob-axes.png", height=4, width=4, units="in", dpi=300)
Logistic regression with probabilities from the side.
df$pred_prob <- predict(m, df, type = "response")
df$pred_link <- predict(m, df, type = "link")
p <- ggplot(df) +
geom_point(aes(x = pred_link, y = pred_prob, colour = y), pch = 21, fill = NA) +
scale_colour_gradient(low = "#00bfc4", high = "#f8766d") +
theme_minimal() +
xlab("0.978 + 0.134 x1 - 1.398 x2") +
ylab("Predicted Probability P(Y=1)") +
geom_vline(xintercept = 0, linetype = "dashed", colour = "gray60") +
geom_hline(yintercept = 0.5, linetype = "dashed", colour = "gray60") +
theme(legend.position = "none")
ggsave(p, filename = "../img/esl-logistic-prob-link.png", height=4, width=5, units="in", dpi=300)
KNN with K=15
xy.grid <- expand.grid(x.grid, y.grid)
names(xy.grid) <- c("x1", "x2")
m <- knn(df[,1:2], xy.grid, as.factor(df[,3]), k=15, prob=TRUE)
xy.grid$yhat <- as.numeric(as.character(m))
p <- ggplot(df) +
geom_point(aes(x = x1, y = x2, colour = y > 0.5), pch = 21, fill = NA) +
geom_point(aes(x = x1, y = x2, colour = yhat > 0.5), alpha = 0.5, data = xy.grid, size = 0.02) +
theme_minimal() +
xlab("Disease Severity Score (x1)") +
ylab("Social Determinants Score (x2)") + theme(axis.text = element_blank()) +
scale_colour_manual(name = "ER Admission?", labels = c("no", "yes"), values = c("#00bfc4", "#f8766d")) +
stat_contour(aes(x = x1, y = x2, z = yhat), breaks = 0.5, data = xy.grid, colour = "gray20") +
theme(legend.position = "none", panel.grid.major = element_blank(), panel.grid.minor = element_blank())
ggsave(p, filename = "../img/esl-knn-15.png", height=4, width=4, units="in", dpi=300)
KNN with K=15 and probabilities
xy.grid <- expand.grid(x.grid, y.grid)
names(xy.grid) <- c("x1", "x2")
m <- knn(df[,1:2], xy.grid, as.factor(df[,3]), k=15, prob=TRUE)
xy.grid$votes <- as.numeric(as.character(attr(m, "prob")))
xy.grid$class <- as.numeric(as.character(m))
xy.grid$yhat <- ifelse(xy.grid$class == 1, xy.grid$votes, 1 - xy.grid$votes)
p <- ggplot(df) +
geom_point(aes(x = x1, y = x2, colour = y), pch = 21, fill = NA) +
geom_point(aes(x = x1, y = x2, colour = yhat), alpha = 1.0, data = xy.grid, size = 0.02) +
scale_colour_gradient(low = "#00bfc4", high = "#f8766d") +
theme_minimal() +
xlab("Disease Severity Score (x1)") +
ylab("Social Determinants Score (x2)") + theme(axis.text = element_blank()) +
stat_contour(aes(x = x1, y = x2, z = yhat), breaks = 0.5, data = xy.grid, colour = "gray20",
linetype = "dashed") +
theme(legend.position = "none", panel.grid.major = element_blank(), panel.grid.minor = element_blank())
ggsave(p, filename = "../img/esl-knn-15-prob.png", height=4, width=4, units="in", dpi=300)
KNN with K=3
xy.grid <- expand.grid(x.grid, y.grid)
names(xy.grid) <- c("x1", "x2")
m <- knn(df[,1:2], xy.grid, as.factor(df[,3]), k=3, prob=TRUE)
xy.grid$yhat <- as.numeric(as.character(m))
p <- ggplot(df) +
geom_point(aes(x = x1, y = x2, colour = y > 0.5), pch = 21, fill = NA) +
geom_point(aes(x = x1, y = x2, colour = yhat > 0.5), alpha = 0.5, data = xy.grid, size = 0.02) +
theme_minimal() +
xlab("Disease Severity Score (x1)") +
ylab("Social Determinants Score (x2)") + theme(axis.text = element_blank()) +
scale_colour_manual(name = "ER Admission?", labels = c("no", "yes"), values = c("#00bfc4", "#f8766d")) +
stat_contour(aes(x = x1, y = x2, z = yhat), breaks = 0.5, data = xy.grid, colour = "gray20") +
theme(legend.position = "none", panel.grid.major = element_blank(), panel.grid.minor = element_blank())
ggsave(p, filename = "../img/esl-knn-3.png", height=4, width=4, units="in", dpi=300)
KNN with K=50
xy.grid <- expand.grid(x.grid, y.grid)
names(xy.grid) <- c("x1", "x2")
m <- knn(df[,1:2], xy.grid, as.factor(df[,3]), k=50, prob=TRUE)
xy.grid$yhat <- as.numeric(as.character(m))
p <- ggplot(df) +
geom_point(aes(x = x1, y = x2, colour = y > 0.5), pch = 21, fill = NA) +
geom_point(aes(x = x1, y = x2, colour = yhat > 0.5), alpha = 0.5, data = xy.grid, size = 0.02) +
theme_minimal() +
xlab("Disease Severity Score (x1)") +
ylab("Social Determinants Score (x2)") + theme(axis.text = element_blank()) +
scale_colour_manual(name = "ER Admission?", labels = c("no", "yes"), values = c("#00bfc4", "#f8766d")) +
stat_contour(aes(x = x1, y = x2, z = yhat), breaks = 0.5, data = xy.grid, colour = "gray20") +
theme(legend.position = "none", panel.grid.major = element_blank(), panel.grid.minor = element_blank())
ggsave(p, filename = "../img/esl-knn-50.png", height=4, width=4, units="in", dpi=300)
KNN with K=100
xy.grid <- expand.grid(x.grid, y.grid)
names(xy.grid) <- c("x1", "x2")
m <- knn(df[,1:2], xy.grid, as.factor(df[,3]), k=100, prob=TRUE)
xy.grid$yhat <- as.numeric(as.character(m))
p <- ggplot(df) +
geom_point(aes(x = x1, y = x2, colour = y > 0.5), pch = 21, fill = NA) +
geom_point(aes(x = x1, y = x2, colour = yhat > 0.5), alpha = 0.5, data = xy.grid, size = 0.02) +
theme_minimal() +
xlab("Disease Severity Score (x1)") +
ylab("Social Determinants Score (x2)") + theme(axis.text = element_blank()) +
scale_colour_manual(name = "ER Admission?", labels = c("no", "yes"), values = c("#00bfc4", "#f8766d")) +
stat_contour(aes(x = x1, y = x2, z = yhat), breaks = 0.5, data = xy.grid, colour = "gray20") +
theme(legend.position = "none", panel.grid.major = element_blank(), panel.grid.minor = element_blank())
ggsave(p, filename = "../img/esl-knn-100.png", height=4, width=4, units="in", dpi=300)
Decision tree
xy.grid <- expand.grid(x.grid, y.grid)
names(xy.grid) <- c("x1", "x2")
m <- rpart(as.factor(y) ~ x1 + x2, data = df)
xy.grid$yhat <- predict(m, xy.grid)[,2]
p <- ggplot(df) +
geom_point(aes(x = x1, y = x2, colour = y > 0.5), pch = 21, fill = NA) +
geom_point(aes(x = x1, y = x2, colour = yhat > 0.5), alpha = 0.5, data = xy.grid, size = 0.02) +
theme_minimal() +
xlab("Disease Severity Score (x1)") +
ylab("Social Determinants Score (x2)") + theme(axis.text = element_blank()) +
scale_colour_manual(name = "ER Admission?", labels = c("no", "yes"), values = c("#00bfc4", "#f8766d")) +
stat_contour(aes(x = x1, y = x2, z = yhat), breaks = 0.5, data = xy.grid, colour = "gray20") +
theme(legend.position = "none", panel.grid.major = element_blank(), panel.grid.minor = element_blank())
ggsave(p, filename = "../img/esl-decision-tree.png", height=4, width=4, units="in", dpi=300)
Plot the decision tree itself
palette <- colorRampPalette(colors=c("#00BFC4", "#F8766D"))
png(filename = "../img/esl-decision-tree-just-tree.png", height=4, width=4, units="in", res=300)
rpart.plot(m, box.palette=palette(20))
dev.off()
null device
1
Decision tree with probabilities
xy.grid <- expand.grid(x.grid, y.grid)
names(xy.grid) <- c("x1", "x2")
m <- rpart(as.factor(y) ~ x1 + x2, data = df)
xy.grid$yhat <- predict(m, xy.grid)[,2]
p <- ggplot(df) +
geom_point(aes(x = x1, y = x2, colour = y), pch = 21, fill = NA) +
geom_point(aes(x = x1, y = x2, colour = yhat), alpha = 1.0, data = xy.grid, size = 0.02) +
scale_colour_gradient(low = "#00bfc4", high = "#f8766d") +
theme_minimal() +
xlab("Disease Severity Score (x1)") +
ylab("Social Determinants Score (x2)") + theme(axis.text = element_blank()) +
stat_contour(aes(x = x1, y = x2, z = yhat), breaks = 0.5, data = xy.grid, colour = "gray20",
linetype = "dashed") +
theme(legend.position = "none", panel.grid.major = element_blank(), panel.grid.minor = element_blank())
ggsave(p, filename = "../img/esl-decision-tree-prob.png", height=4, width=4, units="in", dpi=300)
Random forest
library(randomForest)
m <- randomForest(as.factor(y) ~ x1 + x2, data = df, do.trace = FALSE)
xy.grid <- expand.grid(x.grid, y.grid)
names(xy.grid) <- c("x1", "x2")
xy.grid$yhat <- predict(m, xy.grid, type = "prob")[,2]
p <- ggplot(df) +
geom_point(aes(x = x1, y = x2, colour = y > 0.5), pch = 21, fill = NA) +
geom_point(aes(x = x1, y = x2, colour = yhat > 0.5), alpha = 0.5, data = xy.grid, size = 0.02) +
theme_minimal() +
xlab("Disease Severity Score (x1)") +
ylab("Social Determinants Score (x2)") + theme(axis.text = element_blank()) +
scale_colour_manual(name = "ER Admission?", labels = c("no", "yes"), values = c("#00bfc4", "#f8766d")) +
stat_contour(aes(x = x1, y = x2, z = yhat), breaks = 0.5, data = xy.grid, colour = "gray20") +
theme(legend.position = "none", panel.grid.major = element_blank(), panel.grid.minor = element_blank())
ggsave(p, filename = "../img/esl-rf.png", height=4, width=4, units="in", dpi=300)
LS0tCnRpdGxlOiAiUmVncmVzc2lvbiIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIyMgTGlicmFyaWVzCgpgYGB7ciBzZXR1cH0KbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHNjYWxlcykKbGlicmFyeShkcGx5cikKbGlicmFyeShzdXJ2aXZhbCkKbGlicmFyeShzdXJ2bWluZXIpCmxpYnJhcnkobHVicmlkYXRlKQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KHN0cmluZ3IpCmxpYnJhcnkoZm9yY2F0cykKbGlicmFyeSh3ZXNhbmRlcnNvbikKbGlicmFyeShjbGFzcykKbGlicmFyeShycGFydCkKbGlicmFyeShycGFydC5wbG90KQpsaWJyYXJ5KHBsb3RseSkKYGBgCgojIyBFU0wgRGF0YXNldAoKYGBge3J9CmxvYWQoIi4uL2RhdGEvbWl4dHVyZS5leGFtcGxlLlJEYXRhIikKaW50ZXJjZXB0ID0gNTAuMApjb2VmX3gxID0gMTAuMApjb2VmX3gyID0gLTAuMApzZXQuc2VlZCgxMDAwKQpkZiA8LSBkYXRhLmZyYW1lKHgxID0gbWl4dHVyZS5leGFtcGxlJHhbLDFdLCAKICAgICAgICAgICAgICAgICB4MiA9IG1peHR1cmUuZXhhbXBsZSR4WywyXSkgJT4lCiAgbXV0YXRlKHkgPSBpbnRlcmNlcHQgKyBjb2VmX3gxICogeDEgKyBjb2VmX3gyICogeDIgKyBybm9ybShsZW5ndGgoeDEpLCAwLCA1KSkKeC5ncmlkIDwtIHNlcShtaW4oZGYkeDEpLCBtYXgoZGYkeDEpLCAwLjEpCnkuZ3JpZCA8LSBzZXEobWluKGRmJHgyKSwgbWF4KGRmJHgyKSwgMC4xKQpoaXN0KGRmJHkpCmBgYAoKRmlndXJlIChqdXN0IHRoZSBkYXRhc2V0KQoKYGBge3J9Cnh5LmdyaWQgPC0gZXhwYW5kLmdyaWQoeC5ncmlkLCB5LmdyaWQpCm5hbWVzKHh5LmdyaWQpIDwtIGMoIngxIiwgIngyIikKcCA8LSBnZ3Bsb3QoZGYpICsgCiAgZ2VvbV9wb2ludChhZXMoeCA9IHgxLCB5ID0geDIsIGNvbG91ciA9IHkpLCBwY2ggPSAyMSwgZmlsbCA9IE5BKSArIAogIGdlb21fcG9pbnQoYWVzKHggPSB4MSwgeSA9IHgyKSwgYWxwaGEgPSAwLjUsIGRhdGEgPSB4eS5ncmlkLCBjb2xvdXIgPSAiZ3JheTUwIiwgc2l6ZSA9IDAuMDIpICsgCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgeGxhYigiRGlzZWFzZSBTZXZlcml0eSBTY29yZSAoeDEpIikgKyAKICB5bGFiKCJTb2NpYWwgRGV0ZXJtaW5hbnRzIFNjb3JlICh4MikiKSArIAogIHRoZW1lKGF4aXMudGV4dCA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICBzY2FsZV9jb2xvdXJfZ3JhZGllbnQobmFtZSA9ICJSZWN1cnJlbmNlXG5CaW9tYXJrZXIgKG5nL2RMKSIsIGxvdyA9ICIjMDBiZmM0IiwgaGlnaCA9ICIjZjg3NjZkIikKcHJpbnQocCkKZ2dzYXZlKHAsIGZpbGVuYW1lID0gIi4uL2ltZy9lc2wtcmVnLWp1c3QtZGF0YS5wbmciLCBoZWlnaHQ9NC41LCB3aWR0aD00LCB1bml0cz0iaW4iLCBkcGk9MzAwKQpgYGAKCjNEIHBsb3Qgd2l0aCBLTk4gKEs9MTUpIGFzIDNyZCBkaW1lbnNpb24KCmBgYHtyfQpwIDwtIHBsb3RfbHkoCiAgZGYsIHggPSB+eDEsIHkgPSB+eDIsIHogPSB+eSwgCiAgY29sb3IgPSB+eSwgY29sb3JzID0gY29sb3JSYW1wKGMoIiMwMGJmYzQiLCAiI2Y4NzY2ZCIpKSkgJT4lCiAgYWRkX21hcmtlcnMoKSAlPiUKICBsYXlvdXQoCiAgICBzY2VuZSA9IGxpc3QoeGF4aXMgPSBsaXN0KHRpdGxlID0gJ0Rpc2Vhc2UgU2V2ZXJpdHkgU2NvcmUgKHgxKScpLAogICAgICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICdTb2NpYWwgRGV0ZXJtaW5hbnRzIFNjb3JlICh4MiknKSwKICAgICAgICB6YXhpcyA9IGxpc3QodGl0bGUgPSAnUmVjdXJyZW5jZSBCaW9tYXJrZXIgKHkpJykpCiAgICAgICAgKQpwcmludChwKQpgYGAKCgpEZWNpc2lvbiBib3VuZGFyeTogbG9naXN0aWMgcmVncmVzc2lvbgoKYGBge3J9Cm0gPC0gZ2xtKHkgfiB4MSArIHgyLCBkYXRhID0gZGYsIGZhbWlseSA9ICJiaW5vbWlhbCIpCnh5LmdyaWQgPC0gZXhwYW5kLmdyaWQoeC5ncmlkLCB5LmdyaWQpCm5hbWVzKHh5LmdyaWQpIDwtIGMoIngxIiwgIngyIikKeHkuZ3JpZCR5aGF0IDwtIHByZWRpY3QobSwgeHkuZ3JpZCwgdHlwZSA9ICJyZXNwb25zZSIpCnAgPC0gZ2dwbG90KGRmKSArIAogIGdlb21fcG9pbnQoYWVzKHggPSB4MSwgeSA9IHgyLCBjb2xvdXIgPSB5ID4gMC41KSwgcGNoID0gMjEsIGZpbGwgPSBOQSkgKyAKICBnZW9tX3BvaW50KGFlcyh4ID0geDEsIHkgPSB4MiwgY29sb3VyID0geWhhdCA+IDAuNSksIGFscGhhID0gMC41LCBkYXRhID0geHkuZ3JpZCwgc2l6ZSA9IDAuMDIpICsgCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgeGxhYigiRGlzZWFzZSBTZXZlcml0eSBTY29yZSAoeDEpIikgKyAKICB5bGFiKCJTb2NpYWwgRGV0ZXJtaW5hbnRzIFNjb3JlICh4MikiKSArIHRoZW1lKGF4aXMudGV4dCA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICBzY2FsZV9jb2xvdXJfbWFudWFsKG5hbWUgPSAiRVIgQWRtaXNzaW9uPyIsIGxhYmVscyA9IGMoIm5vIiwgInllcyIpLCB2YWx1ZXMgPSBjKCIjMDBiZmM0IiwgIiNmODc2NmQiKSkgKwogIHN0YXRfY29udG91cihhZXMoeCA9IHgxLCB5ID0geDIsIHogPSB5aGF0KSwgYnJlYWtzID0gMC41LCBkYXRhID0geHkuZ3JpZCwgY29sb3VyID0gImdyYXkyMCIpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLCBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpKQpnZ3NhdmUocCwgZmlsZW5hbWUgPSAiLi4vaW1nL2VzbC1sb2dpc3RpYy5wbmciLCBoZWlnaHQ9NCwgd2lkdGg9NCwgdW5pdHM9ImluIiwgZHBpPTMwMCkKYGBgCgpMb2dpc3RpYyByZWdyZXNzaW9uIHdpdGggcHJvYmFiaWxpdGllcy4KCmBgYHtyfQptIDwtIGdsbSh5IH4geDEgKyB4MiwgZGF0YSA9IGRmLCBmYW1pbHkgPSAiYmlub21pYWwiKQp4eS5ncmlkIDwtIGV4cGFuZC5ncmlkKHguZ3JpZCwgeS5ncmlkKQpuYW1lcyh4eS5ncmlkKSA8LSBjKCJ4MSIsICJ4MiIpCnh5LmdyaWQkeWhhdCA8LSBwcmVkaWN0KG0sIHh5LmdyaWQsIHR5cGUgPSAicmVzcG9uc2UiKQpwIDwtIGdncGxvdChkZikgKyAKICBnZW9tX3BvaW50KGFlcyh4ID0geDEsIHkgPSB4MiwgY29sb3VyID0geSksIHBjaCA9IDIxLCBmaWxsID0gTkEpICsgCiAgZ2VvbV9wb2ludChhZXMoeCA9IHgxLCB5ID0geDIsIGNvbG91ciA9IHloYXQpLCBhbHBoYSA9IDEuMCwgZGF0YSA9IHh5LmdyaWQsIHNpemUgPSAwLjAyKSArIAogIHNjYWxlX2NvbG91cl9ncmFkaWVudChsb3cgPSAiIzAwYmZjNCIsIGhpZ2ggPSAiI2Y4NzY2ZCIpICsKICB0aGVtZV9taW5pbWFsKCkgKyAKICB4bGFiKCJEaXNlYXNlIFNldmVyaXR5IFNjb3JlICh4MSkiKSArIAogIHlsYWIoIlNvY2lhbCBEZXRlcm1pbmFudHMgU2NvcmUgKHgyKSIpICsgdGhlbWUoYXhpcy50ZXh0ID0gZWxlbWVudF9ibGFuaygpKSArIAogIHN0YXRfY29udG91cihhZXMoeCA9IHgxLCB5ID0geDIsIHogPSB5aGF0KSwgYnJlYWtzID0gMC41LCBkYXRhID0geHkuZ3JpZCwgY29sb3VyID0gImdyYXkyMCIsIAogICAgICAgICAgICAgICBsaW5ldHlwZSA9ICJkYXNoZWQiKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSkKZ2dzYXZlKHAsIGZpbGVuYW1lID0gIi4uL2ltZy9lc2wtbG9naXN0aWMtcHJvYi5wbmciLCBoZWlnaHQ9NCwgd2lkdGg9NCwgdW5pdHM9ImluIiwgZHBpPTMwMCkKYGBgCgpMb2dpc3RpYyByZWdyZXNzaW9uIHdpdGggcHJvYmFiaWxpdGllcyBhbmQgYXhpcyBsYWJlbHMuCgpgYGB7cn0KbSA8LSBnbG0oeSB+IHgxICsgeDIsIGRhdGEgPSBkZiwgZmFtaWx5ID0gImJpbm9taWFsIikKeHkuZ3JpZCA8LSBleHBhbmQuZ3JpZCh4LmdyaWQsIHkuZ3JpZCkKbmFtZXMoeHkuZ3JpZCkgPC0gYygieDEiLCAieDIiKQp4eS5ncmlkJHloYXQgPC0gcHJlZGljdChtLCB4eS5ncmlkLCB0eXBlID0gInJlc3BvbnNlIikKcCA8LSBnZ3Bsb3QoZGYpICsgCiAgZ2VvbV9wb2ludChhZXMoeCA9IHgxLCB5ID0geDIsIGNvbG91ciA9IHkpLCBwY2ggPSAyMSwgZmlsbCA9IE5BKSArIAogIGdlb21fcG9pbnQoYWVzKHggPSB4MSwgeSA9IHgyLCBjb2xvdXIgPSB5aGF0KSwgYWxwaGEgPSAxLjAsIGRhdGEgPSB4eS5ncmlkLCBzaXplID0gMC4wMikgKyAKICBzY2FsZV9jb2xvdXJfZ3JhZGllbnQobG93ID0gIiMwMGJmYzQiLCBoaWdoID0gIiNmODc2NmQiKSArCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgeGxhYigiRGlzZWFzZSBTZXZlcml0eSBTY29yZSAoeDEpIikgKyAKICB5bGFiKCJTb2NpYWwgRGV0ZXJtaW5hbnRzIFNjb3JlICh4MikiKSArIAogIHN0YXRfY29udG91cihhZXMoeCA9IHgxLCB5ID0geDIsIHogPSB5aGF0KSwgYnJlYWtzID0gMC41LCBkYXRhID0geHkuZ3JpZCwgY29sb3VyID0gImdyYXkyMCIsIAogICAgICAgICAgICAgICBsaW5ldHlwZSA9ICJkYXNoZWQiKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKZ2dzYXZlKHAsIGZpbGVuYW1lID0gIi4uL2ltZy9lc2wtbG9naXN0aWMtcHJvYi1heGVzLnBuZyIsIGhlaWdodD00LCB3aWR0aD00LCB1bml0cz0iaW4iLCBkcGk9MzAwKQpgYGAKCkxvZ2lzdGljIHJlZ3Jlc3Npb24gd2l0aCBwcm9iYWJpbGl0aWVzIGZyb20gdGhlIHNpZGUuCgpgYGB7cn0KZGYkcHJlZF9wcm9iIDwtIHByZWRpY3QobSwgZGYsIHR5cGUgPSAicmVzcG9uc2UiKQpkZiRwcmVkX2xpbmsgPC0gcHJlZGljdChtLCBkZiwgdHlwZSA9ICJsaW5rIikKcCA8LSBnZ3Bsb3QoZGYpICsgCiAgZ2VvbV9wb2ludChhZXMoeCA9IHByZWRfbGluaywgeSA9IHByZWRfcHJvYiwgY29sb3VyID0geSksIHBjaCA9IDIxLCBmaWxsID0gTkEpICsgCiAgc2NhbGVfY29sb3VyX2dyYWRpZW50KGxvdyA9ICIjMDBiZmM0IiwgaGlnaCA9ICIjZjg3NjZkIikgKwogIHRoZW1lX21pbmltYWwoKSArIAogIHhsYWIoIjAuOTc4ICsgMC4xMzQgeDEgLSAxLjM5OCB4MiIpICsgCiAgeWxhYigiUHJlZGljdGVkIFByb2JhYmlsaXR5IFAoWT0xKSIpICsgCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMCwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3VyID0gImdyYXk2MCIpICsgCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMC41LCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvdXIgPSAiZ3JheTYwIikgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmdnc2F2ZShwLCBmaWxlbmFtZSA9ICIuLi9pbWcvZXNsLWxvZ2lzdGljLXByb2ItbGluay5wbmciLCBoZWlnaHQ9NCwgd2lkdGg9NSwgdW5pdHM9ImluIiwgZHBpPTMwMCkKYGBgCgpLTk4gd2l0aCBLPTE1CgpgYGB7cn0KeHkuZ3JpZCA8LSBleHBhbmQuZ3JpZCh4LmdyaWQsIHkuZ3JpZCkKbmFtZXMoeHkuZ3JpZCkgPC0gYygieDEiLCAieDIiKQptIDwtIGtubihkZlssMToyXSwgeHkuZ3JpZCwgYXMuZmFjdG9yKGRmWywzXSksIGs9MTUsIHByb2I9VFJVRSkKeHkuZ3JpZCR5aGF0IDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKG0pKQpwIDwtIGdncGxvdChkZikgKyAKICBnZW9tX3BvaW50KGFlcyh4ID0geDEsIHkgPSB4MiwgY29sb3VyID0geSA+IDAuNSksIHBjaCA9IDIxLCBmaWxsID0gTkEpICsgCiAgZ2VvbV9wb2ludChhZXMoeCA9IHgxLCB5ID0geDIsIGNvbG91ciA9IHloYXQgPiAwLjUpLCBhbHBoYSA9IDAuNSwgZGF0YSA9IHh5LmdyaWQsIHNpemUgPSAwLjAyKSArIAogIHRoZW1lX21pbmltYWwoKSArIAogIHhsYWIoIkRpc2Vhc2UgU2V2ZXJpdHkgU2NvcmUgKHgxKSIpICsgCiAgeWxhYigiU29jaWFsIERldGVybWluYW50cyBTY29yZSAoeDIpIikgKyB0aGVtZShheGlzLnRleHQgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgc2NhbGVfY29sb3VyX21hbnVhbChuYW1lID0gIkVSIEFkbWlzc2lvbj8iLCBsYWJlbHMgPSBjKCJubyIsICJ5ZXMiKSwgdmFsdWVzID0gYygiIzAwYmZjNCIsICIjZjg3NjZkIikpICsKICBzdGF0X2NvbnRvdXIoYWVzKHggPSB4MSwgeSA9IHgyLCB6ID0geWhhdCksIGJyZWFrcyA9IDAuNSwgZGF0YSA9IHh5LmdyaWQsIGNvbG91ciA9ICJncmF5MjAiKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSkgCmdnc2F2ZShwLCBmaWxlbmFtZSA9ICIuLi9pbWcvZXNsLWtubi0xNS5wbmciLCBoZWlnaHQ9NCwgd2lkdGg9NCwgdW5pdHM9ImluIiwgZHBpPTMwMCkKYGBgCgpLTk4gd2l0aCBLPTE1IGFuZCBwcm9iYWJpbGl0aWVzCgpgYGB7cn0KeHkuZ3JpZCA8LSBleHBhbmQuZ3JpZCh4LmdyaWQsIHkuZ3JpZCkKbmFtZXMoeHkuZ3JpZCkgPC0gYygieDEiLCAieDIiKQptIDwtIGtubihkZlssMToyXSwgeHkuZ3JpZCwgYXMuZmFjdG9yKGRmWywzXSksIGs9MTUsIHByb2I9VFJVRSkKeHkuZ3JpZCR2b3RlcyA8LSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihhdHRyKG0sICJwcm9iIikpKQp4eS5ncmlkJGNsYXNzIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKG0pKQp4eS5ncmlkJHloYXQgPC0gaWZlbHNlKHh5LmdyaWQkY2xhc3MgPT0gMSwgeHkuZ3JpZCR2b3RlcywgMSAtIHh5LmdyaWQkdm90ZXMpCnAgPC0gZ2dwbG90KGRmKSArIAogIGdlb21fcG9pbnQoYWVzKHggPSB4MSwgeSA9IHgyLCBjb2xvdXIgPSB5KSwgcGNoID0gMjEsIGZpbGwgPSBOQSkgKyAKICBnZW9tX3BvaW50KGFlcyh4ID0geDEsIHkgPSB4MiwgY29sb3VyID0geWhhdCksIGFscGhhID0gMS4wLCBkYXRhID0geHkuZ3JpZCwgc2l6ZSA9IDAuMDIpICsgCiAgc2NhbGVfY29sb3VyX2dyYWRpZW50KGxvdyA9ICIjMDBiZmM0IiwgaGlnaCA9ICIjZjg3NjZkIikgKwogIHRoZW1lX21pbmltYWwoKSArIAogIHhsYWIoIkRpc2Vhc2UgU2V2ZXJpdHkgU2NvcmUgKHgxKSIpICsgCiAgeWxhYigiU29jaWFsIERldGVybWluYW50cyBTY29yZSAoeDIpIikgKyB0aGVtZShheGlzLnRleHQgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgc3RhdF9jb250b3VyKGFlcyh4ID0geDEsIHkgPSB4MiwgeiA9IHloYXQpLCBicmVha3MgPSAwLjUsIGRhdGEgPSB4eS5ncmlkLCBjb2xvdXIgPSAiZ3JheTIwIiwgCiAgICAgICAgICAgICAgIGxpbmV0eXBlID0gImRhc2hlZCIpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLCBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpKSAKZ2dzYXZlKHAsIGZpbGVuYW1lID0gIi4uL2ltZy9lc2wta25uLTE1LXByb2IucG5nIiwgaGVpZ2h0PTQsIHdpZHRoPTQsIHVuaXRzPSJpbiIsIGRwaT0zMDApCmBgYAoKS05OIHdpdGggSz0zCgpgYGB7cn0KeHkuZ3JpZCA8LSBleHBhbmQuZ3JpZCh4LmdyaWQsIHkuZ3JpZCkKbmFtZXMoeHkuZ3JpZCkgPC0gYygieDEiLCAieDIiKQptIDwtIGtubihkZlssMToyXSwgeHkuZ3JpZCwgYXMuZmFjdG9yKGRmWywzXSksIGs9MywgcHJvYj1UUlVFKQp4eS5ncmlkJHloYXQgPC0gYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIobSkpCnAgPC0gZ2dwbG90KGRmKSArIAogIGdlb21fcG9pbnQoYWVzKHggPSB4MSwgeSA9IHgyLCBjb2xvdXIgPSB5ID4gMC41KSwgcGNoID0gMjEsIGZpbGwgPSBOQSkgKyAKICBnZW9tX3BvaW50KGFlcyh4ID0geDEsIHkgPSB4MiwgY29sb3VyID0geWhhdCA+IDAuNSksIGFscGhhID0gMC41LCBkYXRhID0geHkuZ3JpZCwgc2l6ZSA9IDAuMDIpICsgCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgeGxhYigiRGlzZWFzZSBTZXZlcml0eSBTY29yZSAoeDEpIikgKyAKICB5bGFiKCJTb2NpYWwgRGV0ZXJtaW5hbnRzIFNjb3JlICh4MikiKSArIHRoZW1lKGF4aXMudGV4dCA9IGVsZW1lbnRfYmxhbmsoKSkgKyAKICBzY2FsZV9jb2xvdXJfbWFudWFsKG5hbWUgPSAiRVIgQWRtaXNzaW9uPyIsIGxhYmVscyA9IGMoIm5vIiwgInllcyIpLCB2YWx1ZXMgPSBjKCIjMDBiZmM0IiwgIiNmODc2NmQiKSkgKwogIHN0YXRfY29udG91cihhZXMoeCA9IHgxLCB5ID0geDIsIHogPSB5aGF0KSwgYnJlYWtzID0gMC41LCBkYXRhID0geHkuZ3JpZCwgY29sb3VyID0gImdyYXkyMCIpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLCBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpKSAKZ2dzYXZlKHAsIGZpbGVuYW1lID0gIi4uL2ltZy9lc2wta25uLTMucG5nIiwgaGVpZ2h0PTQsIHdpZHRoPTQsIHVuaXRzPSJpbiIsIGRwaT0zMDApCmBgYAoKS05OIHdpdGggSz01MAoKYGBge3J9Cnh5LmdyaWQgPC0gZXhwYW5kLmdyaWQoeC5ncmlkLCB5LmdyaWQpCm5hbWVzKHh5LmdyaWQpIDwtIGMoIngxIiwgIngyIikKbSA8LSBrbm4oZGZbLDE6Ml0sIHh5LmdyaWQsIGFzLmZhY3RvcihkZlssM10pLCBrPTUwLCBwcm9iPVRSVUUpCnh5LmdyaWQkeWhhdCA8LSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihtKSkKcCA8LSBnZ3Bsb3QoZGYpICsgCiAgZ2VvbV9wb2ludChhZXMoeCA9IHgxLCB5ID0geDIsIGNvbG91ciA9IHkgPiAwLjUpLCBwY2ggPSAyMSwgZmlsbCA9IE5BKSArIAogIGdlb21fcG9pbnQoYWVzKHggPSB4MSwgeSA9IHgyLCBjb2xvdXIgPSB5aGF0ID4gMC41KSwgYWxwaGEgPSAwLjUsIGRhdGEgPSB4eS5ncmlkLCBzaXplID0gMC4wMikgKyAKICB0aGVtZV9taW5pbWFsKCkgKyAKICB4bGFiKCJEaXNlYXNlIFNldmVyaXR5IFNjb3JlICh4MSkiKSArIAogIHlsYWIoIlNvY2lhbCBEZXRlcm1pbmFudHMgU2NvcmUgKHgyKSIpICsgdGhlbWUoYXhpcy50ZXh0ID0gZWxlbWVudF9ibGFuaygpKSArIAogIHNjYWxlX2NvbG91cl9tYW51YWwobmFtZSA9ICJFUiBBZG1pc3Npb24/IiwgbGFiZWxzID0gYygibm8iLCAieWVzIiksIHZhbHVlcyA9IGMoIiMwMGJmYzQiLCAiI2Y4NzY2ZCIpKSArCiAgc3RhdF9jb250b3VyKGFlcyh4ID0geDEsIHkgPSB4MiwgeiA9IHloYXQpLCBicmVha3MgPSAwLjUsIGRhdGEgPSB4eS5ncmlkLCBjb2xvdXIgPSAiZ3JheTIwIikgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkpIApnZ3NhdmUocCwgZmlsZW5hbWUgPSAiLi4vaW1nL2VzbC1rbm4tNTAucG5nIiwgaGVpZ2h0PTQsIHdpZHRoPTQsIHVuaXRzPSJpbiIsIGRwaT0zMDApCmBgYAoKS05OIHdpdGggSz0xMDAKCmBgYHtyfQp4eS5ncmlkIDwtIGV4cGFuZC5ncmlkKHguZ3JpZCwgeS5ncmlkKQpuYW1lcyh4eS5ncmlkKSA8LSBjKCJ4MSIsICJ4MiIpCm0gPC0ga25uKGRmWywxOjJdLCB4eS5ncmlkLCBhcy5mYWN0b3IoZGZbLDNdKSwgaz0xMDAsIHByb2I9VFJVRSkKeHkuZ3JpZCR5aGF0IDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKG0pKQpwIDwtIGdncGxvdChkZikgKyAKICBnZW9tX3BvaW50KGFlcyh4ID0geDEsIHkgPSB4MiwgY29sb3VyID0geSA+IDAuNSksIHBjaCA9IDIxLCBmaWxsID0gTkEpICsgCiAgZ2VvbV9wb2ludChhZXMoeCA9IHgxLCB5ID0geDIsIGNvbG91ciA9IHloYXQgPiAwLjUpLCBhbHBoYSA9IDAuNSwgZGF0YSA9IHh5LmdyaWQsIHNpemUgPSAwLjAyKSArIAogIHRoZW1lX21pbmltYWwoKSArIAogIHhsYWIoIkRpc2Vhc2UgU2V2ZXJpdHkgU2NvcmUgKHgxKSIpICsgCiAgeWxhYigiU29jaWFsIERldGVybWluYW50cyBTY29yZSAoeDIpIikgKyB0aGVtZShheGlzLnRleHQgPSBlbGVtZW50X2JsYW5rKCkpICsgCiAgc2NhbGVfY29sb3VyX21hbnVhbChuYW1lID0gIkVSIEFkbWlzc2lvbj8iLCBsYWJlbHMgPSBjKCJubyIsICJ5ZXMiKSwgdmFsdWVzID0gYygiIzAwYmZjNCIsICIjZjg3NjZkIikpICsKICBzdGF0X2NvbnRvdXIoYWVzKHggPSB4MSwgeSA9IHgyLCB6ID0geWhhdCksIGJyZWFrcyA9IDAuNSwgZGF0YSA9IHh5LmdyaWQsIGNvbG91ciA9ICJncmF5MjAiKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSkgCmdnc2F2ZShwLCBmaWxlbmFtZSA9ICIuLi9pbWcvZXNsLWtubi0xMDAucG5nIiwgaGVpZ2h0PTQsIHdpZHRoPTQsIHVuaXRzPSJpbiIsIGRwaT0zMDApCmBgYAoKRGVjaXNpb24gdHJlZQoKYGBge3J9Cnh5LmdyaWQgPC0gZXhwYW5kLmdyaWQoeC5ncmlkLCB5LmdyaWQpCm5hbWVzKHh5LmdyaWQpIDwtIGMoIngxIiwgIngyIikKbSA8LSBycGFydChhcy5mYWN0b3IoeSkgfiB4MSArIHgyLCBkYXRhID0gZGYpCnh5LmdyaWQkeWhhdCA8LSBwcmVkaWN0KG0sIHh5LmdyaWQpWywyXQpwIDwtIGdncGxvdChkZikgKyAKICBnZW9tX3BvaW50KGFlcyh4ID0geDEsIHkgPSB4MiwgY29sb3VyID0geSA+IDAuNSksIHBjaCA9IDIxLCBmaWxsID0gTkEpICsgCiAgZ2VvbV9wb2ludChhZXMoeCA9IHgxLCB5ID0geDIsIGNvbG91ciA9IHloYXQgPiAwLjUpLCBhbHBoYSA9IDAuNSwgZGF0YSA9IHh5LmdyaWQsIHNpemUgPSAwLjAyKSArIAogIHRoZW1lX21pbmltYWwoKSArIAogIHhsYWIoIkRpc2Vhc2UgU2V2ZXJpdHkgU2NvcmUgKHgxKSIpICsgCiAgeWxhYigiU29jaWFsIERldGVybWluYW50cyBTY29yZSAoeDIpIikgKyB0aGVtZShheGlzLnRleHQgPSBlbGVtZW50X2JsYW5rKCkpICsKICBzY2FsZV9jb2xvdXJfbWFudWFsKG5hbWUgPSAiRVIgQWRtaXNzaW9uPyIsIGxhYmVscyA9IGMoIm5vIiwgInllcyIpLCB2YWx1ZXMgPSBjKCIjMDBiZmM0IiwgIiNmODc2NmQiKSkgKwogIHN0YXRfY29udG91cihhZXMoeCA9IHgxLCB5ID0geDIsIHogPSB5aGF0KSwgYnJlYWtzID0gMC41LCBkYXRhID0geHkuZ3JpZCwgY29sb3VyID0gImdyYXkyMCIpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLCBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpKSAKZ2dzYXZlKHAsIGZpbGVuYW1lID0gIi4uL2ltZy9lc2wtZGVjaXNpb24tdHJlZS5wbmciLCBoZWlnaHQ9NCwgd2lkdGg9NCwgdW5pdHM9ImluIiwgZHBpPTMwMCkKYGBgCgpQbG90IHRoZSBkZWNpc2lvbiB0cmVlIGl0c2VsZgoKYGBge3J9CnBhbGV0dGUgPC0gY29sb3JSYW1wUGFsZXR0ZShjb2xvcnM9YygiIzAwQkZDNCIsICIjRjg3NjZEIikpCnBuZyhmaWxlbmFtZSA9ICIuLi9pbWcvZXNsLWRlY2lzaW9uLXRyZWUtanVzdC10cmVlLnBuZyIsIGhlaWdodD00LCB3aWR0aD00LCB1bml0cz0iaW4iLCByZXM9MzAwKQpycGFydC5wbG90KG0sIGJveC5wYWxldHRlPXBhbGV0dGUoMjApKQpkZXYub2ZmKCkKYGBgCgpEZWNpc2lvbiB0cmVlIHdpdGggcHJvYmFiaWxpdGllcwoKYGBge3J9Cnh5LmdyaWQgPC0gZXhwYW5kLmdyaWQoeC5ncmlkLCB5LmdyaWQpCm5hbWVzKHh5LmdyaWQpIDwtIGMoIngxIiwgIngyIikKbSA8LSBycGFydChhcy5mYWN0b3IoeSkgfiB4MSArIHgyLCBkYXRhID0gZGYpCnh5LmdyaWQkeWhhdCA8LSBwcmVkaWN0KG0sIHh5LmdyaWQpWywyXQpwIDwtIGdncGxvdChkZikgKyAKICBnZW9tX3BvaW50KGFlcyh4ID0geDEsIHkgPSB4MiwgY29sb3VyID0geSksIHBjaCA9IDIxLCBmaWxsID0gTkEpICsgCiAgZ2VvbV9wb2ludChhZXMoeCA9IHgxLCB5ID0geDIsIGNvbG91ciA9IHloYXQpLCBhbHBoYSA9IDEuMCwgZGF0YSA9IHh5LmdyaWQsIHNpemUgPSAwLjAyKSArIAogIHNjYWxlX2NvbG91cl9ncmFkaWVudChsb3cgPSAiIzAwYmZjNCIsIGhpZ2ggPSAiI2Y4NzY2ZCIpICsKICB0aGVtZV9taW5pbWFsKCkgKyAKICB4bGFiKCJEaXNlYXNlIFNldmVyaXR5IFNjb3JlICh4MSkiKSArIAogIHlsYWIoIlNvY2lhbCBEZXRlcm1pbmFudHMgU2NvcmUgKHgyKSIpICsgdGhlbWUoYXhpcy50ZXh0ID0gZWxlbWVudF9ibGFuaygpKSArIAogIHN0YXRfY29udG91cihhZXMoeCA9IHgxLCB5ID0geDIsIHogPSB5aGF0KSwgYnJlYWtzID0gMC41LCBkYXRhID0geHkuZ3JpZCwgY29sb3VyID0gImdyYXkyMCIsIAogICAgICAgICAgICAgICBsaW5ldHlwZSA9ICJkYXNoZWQiKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSkgCmdnc2F2ZShwLCBmaWxlbmFtZSA9ICIuLi9pbWcvZXNsLWRlY2lzaW9uLXRyZWUtcHJvYi5wbmciLCBoZWlnaHQ9NCwgd2lkdGg9NCwgdW5pdHM9ImluIiwgZHBpPTMwMCkKYGBgCgpSYW5kb20gZm9yZXN0CgpgYGB7cn0KbGlicmFyeShyYW5kb21Gb3Jlc3QpCm0gPC0gcmFuZG9tRm9yZXN0KGFzLmZhY3Rvcih5KSB+IHgxICsgeDIsIGRhdGEgPSBkZiwgZG8udHJhY2UgPSBGQUxTRSkKeHkuZ3JpZCA8LSBleHBhbmQuZ3JpZCh4LmdyaWQsIHkuZ3JpZCkKbmFtZXMoeHkuZ3JpZCkgPC0gYygieDEiLCAieDIiKQp4eS5ncmlkJHloYXQgPC0gcHJlZGljdChtLCB4eS5ncmlkLCB0eXBlID0gInByb2IiKVssMl0KcCA8LSBnZ3Bsb3QoZGYpICsgCiAgZ2VvbV9wb2ludChhZXMoeCA9IHgxLCB5ID0geDIsIGNvbG91ciA9IHkgPiAwLjUpLCBwY2ggPSAyMSwgZmlsbCA9IE5BKSArIAogIGdlb21fcG9pbnQoYWVzKHggPSB4MSwgeSA9IHgyLCBjb2xvdXIgPSB5aGF0ID4gMC41KSwgYWxwaGEgPSAwLjUsIGRhdGEgPSB4eS5ncmlkLCBzaXplID0gMC4wMikgKyAKICB0aGVtZV9taW5pbWFsKCkgKyAKICB4bGFiKCJEaXNlYXNlIFNldmVyaXR5IFNjb3JlICh4MSkiKSArIAogIHlsYWIoIlNvY2lhbCBEZXRlcm1pbmFudHMgU2NvcmUgKHgyKSIpICsgdGhlbWUoYXhpcy50ZXh0ID0gZWxlbWVudF9ibGFuaygpKSArIAogIHNjYWxlX2NvbG91cl9tYW51YWwobmFtZSA9ICJFUiBBZG1pc3Npb24/IiwgbGFiZWxzID0gYygibm8iLCAieWVzIiksIHZhbHVlcyA9IGMoIiMwMGJmYzQiLCAiI2Y4NzY2ZCIpKSArCiAgc3RhdF9jb250b3VyKGFlcyh4ID0geDEsIHkgPSB4MiwgeiA9IHloYXQpLCBicmVha3MgPSAwLjUsIGRhdGEgPSB4eS5ncmlkLCBjb2xvdXIgPSAiZ3JheTIwIikgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2JsYW5rKCksIHBhbmVsLmdyaWQubWlub3IgPSBlbGVtZW50X2JsYW5rKCkpCmdnc2F2ZShwLCBmaWxlbmFtZSA9ICIuLi9pbWcvZXNsLXJmLnBuZyIsIGhlaWdodD00LCB3aWR0aD00LCB1bml0cz0iaW4iLCBkcGk9MzAwKQpgYGAKCgo=